import copy
import math
import random
import time
import cal
import config
import numpy as np

# ========================全局配置区===========================
# 种群规模
SIZE = config.SIZE
# 种群
population = []
# 工序数
m = config.m
# 工件种类数量
n = config.n
#  机器数量
M = config.M
# 单个基因变异概率
VP = 0.5
# 全局最小时间
MIN = 1000
# 全局缓冲表
CACHE = {}
individual_count = {}
# 全局时间表
times = np.array([
    [2, 4, 6, 4, 4, 6, 5, 3, 2, 3, 5, 6],
    [2, 5, 5, 3, 3, 5, 5, 2, 5, 5, 6, 2],
    [3, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 4],
    [4, 3, 4, 6, 3, 2, 4, 7, 1, 3, 3, 5],
    [5, 4, 2, 5, 1, 3, 6, 5, 2, 4, 5, 4],
    [2, 3, 3, 3, 3, 3, 4, 3, 3, 7, 4, 6],
    [3, 4, 4, 6, 4, 3, 3, 4, 3, 8, 8, 7],
    [2, 5, 2, 5, 6, 9, 3, 3, 6, 6, 6, 6],
    [3, 4, 5, 8, 5, 5, 5, 5, 4, 5, 4, 5]
]).T


# ====================================================
# 检测当前基因是否在种群中出现了
def isRepeat(x):
    for it in population:
        if np.array_equal(x, it):
            return True
    return False


# 初始化种群
def init():
    global population
    population = []
    for _ in range(0, SIZE):
        x = getNewGene()
        if not isRepeat(x):
            population.append(x)


# 行交叉： 随机选择两个个体进行交叉，交叉的结果是种群规模变成2N
def intersect():
    global population
    population_temp = copy.deepcopy(population)
    while len(population_temp) > 0:
        # 产生随机的两个待交叉的个体 下标
        seek1 = np.random.randint(0, len(population_temp))
        seek2 = np.random.randint(0, len(population_temp))
        if seek1 == seek2:
            continue
        # 交叉位
        seek = np.random.randint(1, n)
        # 将第一个截取成两个部分
        up1 = np.array(population[seek1][:seek])
        down1 = np.array(population[seek1][seek:])
        # 将第二个截取成两个部分
        up2 = np.array(population[seek2][:seek])
        down2 = np.array(population[seek2][seek:])
        new1 = np.vstack((up1, down2))
        new2 = np.vstack((up2, down1))
        population.append(new1)
        population.append(new2)
        population_temp.pop(seek1)
        population_temp.pop(seek2 - 1)


# 随机产生一个合法的基因
def getNewGene():
    # 使用NumPy的random.randint函数生成随机矩阵
    first_col = np.random.randint(1, 4, n) + np.random.randint(0, 10) / 10.0  # 范围为1-3
    second_col = np.random.randint(4, 7, n) + np.random.randint(0, 10) / 10.0  # 范围为4-6r
    third_col = np.random.randint(7, 9, n) + np.random.randint(0, 9) / 10
    matrix = np.column_stack((first_col, second_col, third_col))
    return matrix


# 选择算法 - 领域选择法
def select():
    global population
    newPop = []
    # 按照领域进行选择 适应度大的被保留
    for i in range(0, len(population), 2):
        s1 = cal.calc(population[i])
        s2 = cal.calc(population[i + 1])
        if s1 < s2:
            newPop.append(population[i])
        else:
            newPop.append(population[i + 1])
    population = newPop

# 选择算法 - 轮盘赌选择法
def select1():
    global population
    allFit = 0
    newPop = []
    # 1. 重新分配数据结构
    newP = [[0,0,cal.calc(population[0])]]
    for (i,index) in enumerate(population[1:],start=1):
        # print(newP[i - 1][2],'==',cal.calc(population[i]))
        allFit = newP[i - 1][2] + cal.calc(population[i])
        newP.append([i,newP[i-1][2],allFit])
    newP = np.array(newP,dtype=np.int64)

    # print(newP)
    for i in newP:
        # 生成随机数 0 - allFit
        seek = random.randint(0,allFit)
        if i[1] >= seek and seek <= i[2]:
            newPop.append(population[i[0]])
        # filtered_rows = newP[(newP[:, 1] <= seek) & (seek < newP[:, 2])]
        # print(seek,'====',filtered_rows[0])
        #newPop.append(population[filtered_rows[0]])
    # print(newPop)
    population = newPop
def new_value(x):
    if 1 <= x <= 3:
        new_x = x
        while new_x == x:
            new_x = np.random.randint(1, 4)
        return new_x
    elif 4 <= x <= 6:
        new_x = x
        while new_x == x:
            new_x = np.random.randint(4, 7)
        return new_x
    elif 7 <= x <= 9:
        new_x = x
        while new_x == x:
            new_x = np.random.randint(7, 10)
        return new_x
    else:
        return x  # 如果x不在这些范围内，则返回原始的x值

#  45 100
#  21 3


def mutation():
    global population, MIN
    for i in range(0, len(population)):
        # 更新最短时间
        cul = cal.calc(population[i])
        # if cul not in CACHE:
        #     CACHE[cul] = 1
        # else:
        #     CACHE[cul] = CACHE[cul] + 1
        # while CACHE[cul] > 50:
        #     x = np.random.randint(0, n)
        #     y = np.random.randint(0, 3)
        #     # 整数部分按照固定规则随机变异
        #     newI = new_value(int(population[i][x][y]))
        #
        #     # 生成一个随机小数
        #     newF = np.random.randint(0, 10) / 10
        #     population[i][x][y] = newI + newF
        if cul < MIN:
            MIN = cul
        random_decimal = round(np.random.rand() * 100) / 100
        # print(cul + 1 - MIN)
        # 变异
        if random_decimal < VP:
            x = np.random.randint(0, n)
            y = np.random.randint(0, 3)
            # 整数部分按照固定规则随机变异
            newI = new_value(int(population[i][x][y]))

            # 生成一个随机小数
            newF = np.random.randint(0, 10) / 10
            population[i][x][y] = newI + newF


def AllValue():
    poo = []
    for (i, x) in enumerate(population):
        poo.append([cal.calc(x), i])
    poo.sort()
    print(poo)
    # 输出到文件中

    # cal.toString(population[poo[0][1]])
def mutation1():
    global population, MIN, individual_count

    # 计算每个个体的适应度值
    fitness_values = [cal.calc(individual) for individual in population]

    for i in range(0, len(population)):
        individual = population[i]
        fitness = fitness_values[i]

        # 更新个体计数字典
        if tuple(individual.flatten()) not in individual_count:
            individual_count[tuple(individual.flatten())] = 1
        else:
            individual_count[tuple(individual.flatten())] += 1

        # 根据适应度决定允许的最大重复次数
        max_allowed_repeats = int(math.exp(fitness))

        # 如果个体出现次数超过最大允许次数，则进行变异
        if individual_count[tuple(individual.flatten())] > max_allowed_repeats:
            x = np.random.randint(0, n)
            y = np.random.randint(0, 3)
            # 整数部分按照固定规则随机变异
            newI = new_value(int(individual[x][y]))

            # 生成一个随机小数
            newF = np.random.randint(0, 10) / 10
            individual[x][y] = newI + newF

        # 更新最短时间
        cul = cal.calc(individual)
        if cul < MIN:
            MIN = cul
# 随机产生种群规模为 N 的个体
t = 1000
for j in range(0, 1):
    init()
    begin = time.time()
    for i in range(0, t):
        print("迭代次数：",i)
        #VP = initial_VP * math.exp(-k * i)
        intersect()
        select()
        mutation()
    # print(CACHE)
    AllValue()
    end = time.time()
    print("执行时间：", round(end - begin, 2), 's')
